﻿using System;
using System.Linq;
using RabbitMQ.Client.Events;
using RabbitMQ.Client.MessagePatterns;
using smp.RabbitMq.Strategies;

namespace smp.RabbitMq
{
    internal class Subscriber : RabbitMqBusBase, ISubscriber
    {
        private readonly SubscriberHandlers _handlers;
        private ISubscriberStrategy _strategy;

        internal Subscriber(IRabbitMqConfiguration configuration, SubscriberHandlers handlers, ISubscriberStrategy strategy)
            : base(configuration)
        {
            _handlers = handlers;
            _strategy = strategy;
            InstancesFactory.SubscriberConfiguration = Configuration;
        }

        ~Subscriber()
        {
            Dispose(false);
        }

        #region Implementation of IDisposable

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        #endregion

        #region Implementation of ISubscriber

        public void HostSubscriberServer()
        {
            while (true)
            {
                try
                {
                    Log.Debug("Subscriber declaring subscription to queue.");
                    using (var subscription = new Subscription(Channel, Configuration.Queue, false))
                    {
                        foreach (BasicDeliverEventArgs deliveryEventArgs in subscription)
                        {
                            var packet = deliveryEventArgs.Body;
                            var envelope = GetEnvelopeFromPacket(packet);
                            if (envelope == null)
                            {
                                subscription.Ack(deliveryEventArgs);
                                continue;
                            }

                            Log.Info("Subscriber recieved message [{0}]", envelope.Message);
                            var handlers = _handlers.GetHandlers(envelope.MessageType);
                            if (handlers == null || handlers.Count() == 0)
                            {
                                subscription.Ack(deliveryEventArgs);
                                continue;
                            }

                            var result = _strategy.HandleMessage(handlers, envelope);
                            if (!result.Success && Configuration.ErrorQueue)
                            {
                                Channel.BasicReject(deliveryEventArgs.DeliveryTag, false);
                                continue;
                            }

                            subscription.Ack(deliveryEventArgs);
                        }
                    }
                }
                catch (Exception ex)
                {
                    var error =
                        new Exception("Subscriber failed when trying to subscribe to queue {0} " + Configuration.Queue,
                                      ex);
                    Log.Fatal(error);
                    throw error;
                }
            }
        }

        #endregion
    }
}
